import type { Metadata } from 'next'; import Link from 'next/link'; import { notFound, permanentRedirect } from 'next/navigation'; import { ExternalLink } from 'lucide-react'; import { PageHeader, Section, KV, Note } from '@/components/ui/section'; import { Badge, ClaimBadge } from '@/components/ui/badge'; import { EmptyState } from '@/components/ui/empty-state'; import { Freshness } from '@/components/ui/freshness'; import { Pager } from '@/components/ui/pager'; import { SourceBadge } from '@/components/ui/source-badge'; import { ApprovalsTable } from '@/components/data/approvals-table'; import { EvidenceTable } from '@/components/data/evidence-table'; import { TrialTable } from '@/components/data/trial-list'; import { PublicationList } from '@/components/data/publication-list'; import { GraphLink } from '@/components/graph/graph-link'; import { getBiomarkerBySlug, biomarkerGenes, biomarkerVariants, biomarkerCancers, biomarkerDrugs, biomarkerEvidence, biomarkerEvidenceCount, biomarkerApprovals, biomarkerTrialCounts, biomarkerActiveTrials, literatureScope, noScopeReason, kindLabel, BIOMARKER_LINKS_FORMULA, } from '@/lib/queries/biomarkers'; import { EVIDENCE_PAGE_SIZE, EVIDENCE_LEVEL_LABEL } from '@/lib/queries/evidence'; import { TRIAL_PAGE_SIZE } from '@/lib/queries/trials'; import { recentPublicationsFor, recentPublicationsForCount, PUBLICATION_PAGE_SIZE } from '@/lib/queries/publications'; import { loadProvenance } from '@/lib/queries/provenance'; import { jsonLd } from '@/lib/seo'; import { SITE_URL } from '@/lib/site'; import { fmtDate, fmtInt, humanize } from '@/lib/format'; import { pageInfo } from '@/lib/pagination'; import { int, str, withParams, type SP } from '@/lib/search-params'; export const revalidate = 3600; export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise { const b = await getBiomarkerBySlug((await params).slug); return b ? { title: `${b.name} — biomarker`, description: b.description ?? `${b.name}: what is measured, associated cancers, drugs with predictive evidence, approvals and trials.`, alternates: { canonical: `/biomarker/${b.slug}` } } : { title: 'Biomarker' }; } const EVS = (code: string) => `https://evsexplore.semantics.cancer.gov/evsexplore/concept/ncit/${code}`; export default async function BiomarkerPage({ params, searchParams }: { params: Promise<{ slug: string }>; searchParams: Promise }) { const { slug } = await params; const b = await getBiomarkerBySlug(slug); if (!b) notFound(); if (b.slug !== slug) permanentRedirect(`/biomarker/${b.slug}`); const sp = await searchParams; const m = b.measurement; const hasScope = b.gene_ids.length > 0 || b.variant_ids.length > 0; const reason = noScopeReason(b); const lit = literatureScope(b); const [evTotal, trialCounts, pTotal] = await Promise.all([hasScope ? biomarkerEvidenceCount(b, 'PREDICTIVE') : Promise.resolve(0), hasScope ? biomarkerTrialCounts(b) : Promise.resolve({ total: 0, active: 0, recruiting: 0, phase3: 0 }), lit.ids.length ? recentPublicationsForCount(lit.entityType, lit.ids) : Promise.resolve(0)]); const ev = pageInfo(int(sp, 'evPage', 1, 1, 100_000), EVIDENCE_PAGE_SIZE, evTotal); const tp = pageInfo(int(sp, 'tPage', 1, 1, 100_000), TRIAL_PAGE_SIZE, trialCounts.active); const pp = pageInfo(int(sp, 'pPage', 1, 1, 100_000), PUBLICATION_PAGE_SIZE, pTotal); const aPage = int(sp, 'aPage', 1, 1, 100_000); const [genes, variants, cancers, drugs, evidence, approvals, trials, pubs] = await Promise.all([ biomarkerGenes(b), biomarkerVariants(b), hasScope ? biomarkerCancers(b) : Promise.resolve([]), hasScope ? biomarkerDrugs(b) : Promise.resolve([]), evTotal ? biomarkerEvidence(b, { type: 'PREDICTIVE', page: ev.page, pageSize: ev.pageSize }) : Promise.resolve([]), biomarkerApprovals(b), trialCounts.active ? biomarkerActiveTrials(b, { page: tp.page, pageSize: tp.pageSize }) : Promise.resolve([]), pTotal ? recentPublicationsFor(lit.entityType, lit.ids, { page: pp.page, pageSize: pp.pageSize }) : Promise.resolve([]), ]); const prov = await loadProvenance([...evidence.map((e) => e.provenance_id), ...approvals.map((a) => a.provenance_id)]); const jurisdictions = [...new Set(approvals.map((a) => a.jurisdiction))].sort(); const wanted = str(sp, 'jurisdiction'); const selected = jurisdictions.includes(wanted) ? wanted : null; const shownApprovals = selected ? approvals.filter((a) => a.jurisdiction === selected) : approvals; const tumorAgnosticRows = approvals.filter((a) => a.tumor_agnostic); const byIndication = approvals.filter((a) => a.matched_by === 'indication').length; const current = { jurisdiction: selected ?? '', evPage: ev.page > 1 ? ev.page : '', tPage: tp.page > 1 ? tp.page : '', pPage: pp.page > 1 ? pp.page : '', aPage: aPage > 1 ? aPage : '' }; const href = (o: Record, hash?: string) => `/biomarker/${b.slug}${withParams(current, o)}${hash ? `#${hash}` : ''}`; const aliases = m.aliases ?? []; const ld: Record = { '@context': 'https://schema.org', '@type': 'MedicalTest', name: b.name, url: `${SITE_URL}/biomarker/${b.slug}` }; if (b.description) ld.description = b.description; if (aliases.length) ld.alternateName = aliases.slice(0, 20); if (b.ncit_code) ld.code = [{ '@type': 'MedicalCode', codeValue: b.ncit_code, codingSystem: 'NCIt' }]; if (m.assays?.length) ld.usesDevice = m.assays.map((a) => ({ '@type': 'MedicalDevice', name: a })); const computedNote = (rule: string) => ( rule {BIOMARKER_LINKS_FORMULA} ); return (